# -*- coding: utf-8 -*-
import json
import logging
import decimal
import datetime
import itertools
import operator
import random

from google.appengine.ext import db
from google.appengine.api import mail
from google.appengine.api import memcache

from flaskext import wtf

from signup import DecimalProperty
from payson import *
from celestra import app

ranks = (
#    (50, 'Crewman'),
#    (100, 'Corporal'),
    (500, 'Lieutenant'),
    (1000, 'Captain'),
    (2500, 'Major'),
    (5000, 'Colonel'),
    (10000, 'Commander'),
    (50000, 'Blue Squadron')
          )

rank_dict = dict((v, k) for k, v in ranks)


class InterestForm(wtf.Form):
    firstname = wtf.TextField('First name', validators=[wtf.Required(), ])
    lastname = wtf.TextField('Last name', validators=[wtf.Required(), ])
    email = wtf.html5.EmailField('E-mail', validators=[wtf.Required(), ])
    comments = wtf.TextAreaField('Comments')
    amount = wtf.RadioField('Contribution (SEK)', choices=[
# ('50', u'<strong>50 kr - Crewman:</strong> (%d/&infin; taken) Every penny will contribute to the realization of this project. Perhaps you are planning to play but are uncertain, perhaps you just think this idea rock and want it to become a reality?'),
('500', u'<strong>500 kr - Lieutenant:</strong> (%d/&infin; taken) You will get an email with a special link that allows you to sign up two days before everybody else. (Included in all below)'),
# ('500', u'<strong>500 kr - Lieutenant:</strong> (%d/&infin; taken) We will name a stellar body in the game after you (within fictional limits).'),
('1000', u'<strong>1 000 kr - Captain:</strong> (%d/100 taken) You will get a special invitation to our release party where you will be able to fraternize with the organizers and inspect the progress of our props and software development. (Included in all below)'),
('2500', u'<strong>2 500 kr - Major</strong> (%d/12 taken) You are invited to participate in the game mastering room on any game that you are not playing in to follow the game from the other side - behind the scenes.'),
('5000', u'<strong>5 000 kr - Colonel:</strong> (%d/10 taken) Be among the first to pick in which game you want to participate and what character to play. On top of that you will get a uniform that you can keep after the game.'),
('10000', u'<strong>10 000 kr - Commander:</strong> (%d/5 taken) We will write a custom character for you and your writer will invite you to a dinner in Stockholm or Gothenburg to discuss and develop it. Plus you get to keep your costume.'),
('50000', u'<strong>50 000 kr - Blue Squadron:</strong> (%d/2 taken) You love your friends, and you love playing with your friends. This will make you the most popular person in your friend circle. This reward gives you 12 tickets to any of the games, a personal invitation by the game masters and a custom written back plot for you and your friends to explore.')],
                            validators=[wtf.Optional(), ])
    captcha = wtf.RecaptchaField('Spam blocker*')


class InterestedUser(db.Model):
    firstname = db.StringProperty(required=True)
    lastname = db.StringProperty(required=True)
    email = db.EmailProperty(required=True)
    comments = db.TextProperty()
    date = db.DateProperty(required=True,
                           auto_now_add=True)

#    @db.ComputedProperty
    @property
    def amount(self):
        return sum([d.amount for d in self.donations
                    if d.status == 'COMPLETED'])


class Donation(db.Model):
    token = db.StringProperty()
    amount = DecimalProperty(required=True)
    user = db.ReferenceProperty(InterestedUser, collection_name='donations')
    status = db.StringProperty()
    purchase_id = db.StringProperty()
    sender_email = db.EmailProperty(required=True)
    date = db.DateTimeProperty(required=True, auto_now_add=True)


def total_donated_amount():
    q = db.GqlQuery("SELECT amount FROM Donation WHERE status = 'COMPLETED'")
    return sum([d.amount for d in q])


def make_pay_request_data(memo, tracking_id, sender_email,
                          sender_first_name,
                          sender_last_name, amount, custom):
    if app.config.get('TESTING'):
        host = 'http://localhost:8080'
    else:
        host = 'https://celestra-larp.appspot.com'
    return {
        'returnUrl': '%s/payment/return' % host,
        'cancelUrl': '%s/payment/cancel' % host,
        'ipnNotificationUrl': '%s/payment/ipn' % host,
        'memo': memo,
        'localeCode': 'EN',
        'trackingId': tracking_id,
        'senderEmail': sender_email,
        'senderFirstName': sender_first_name,
        'senderLastName': sender_last_name,
        'receiverList': [Receiver('payments@celestra-larp.com', amount), ],
        'guaranteeOffered': 'NO',
        'custom': custom
        }


def update_donation(paydetails, donation):
    if donation.sender_email == paydetails.senderEmail:
        donation.purchase_id = paydetails.purchaseId
        donation.status = paydetails.status
        donation.put()
        logging.info('Updated donation from {email} to {status} for {amount} kr'\
                     .format(email=paydetails.senderEmail,
                             status=paydetails.status,
                             amount=donation.amount))
    else:
        logging.error('Email mismatch for %s'
              % str(donation.key()))


def get_donation_from_token(args):
    token = args.get('TOKEN', args.get('token', ''))
    q = Donation.all()
    q.filter('token =', token)
    return q.get()

messages = {
'payson error': \
"""There was an error in the communication with our payments provider. Please try to donate later.""",
'registration ok': \
"""Your interest has been recorded. We'll contact you at %s when proper registration opens!""",
'no donation': \
"Please consider coming back and contribute to the fund raising later. Thank You.",
'donation ok': \
"Thank you for contributing %d kr to the project. It really helps.",
'donation canceled': \
"Payment canceled. Please consider coming back and contribute to the fund raising later instead. Thank You.",
'donation error': \
"""Something went wrong handling your payment. Maybe you canceled or maybe you didn't have any money. 
Please consider coming back and contribute to the fund raising later instead. Thank You."""
}

memo = \
"""Crowdfunding contribution to the Celestra Larp project. 
Can be used as participant fee and includes 6% VAT."""

receipt_mail_subj = u'"The Monitor Celestra" interest form confirmation'
receipt_mail = u"""{name},

Thank you for showing you interest in "The Monitor Celestra". We will contact \
you at this address when proper registration opens.

One thing you can do for the project right now is help us spread the word. We \
do not have much in the way of a marketing budget so we rely on people \
like you to build a large enough interest. E g you can use this link to share a \
link to our website on Facebook: http://www.facebook.com/sharer.php?u=http%3A//www.celestra-larp.com

{contribute}

If you like to get in touch with us feel free to contact us by replying to this email. \
And remember to follow us on Twitter @CelestraLarp for the lastest updates.

So say we all!

/The Celestra Crew

"""
contributed = u"""We also thank you for your total contribution of %d kr to our \
funding drive. It really helps."""
no_contribution = u"""Another thing you can do is to return to https://celestra-larp.appspot.com/interest \
and contribute to our funding drive. The project depends on contributions from future participants like you."""
contribute_more = u"""Also remember that you can return to https://celestra-larp.appspot.com/interest \
and make another contribution at any time."""


def send_receipt_mail(user):
    if user.amount == 0:
        c = no_contribution
    elif user.amount >= 1000:
        c = contributed % user.amount
    else:
        c = contribute_more
    rank = get_rank(int(user.amount))
    title = [rank, ] if rank else []
    name = title + [user.firstname, user.lastname]
    b = receipt_mail.format(name=' '.join(name),
                            contribute=c)
    mail.send_mail(sender=u"The Monitor Celestra Crew <info@celestra-larp.com>",
                   to="%s %s <%s>" % (user.firstname, user.lastname, user.email),
                   subject=receipt_mail_subj,
                   body=b)


def get_rank(amount):
    for a, r in reversed(ranks):
        if amount >= a:
            return r
    return ''


def rank_quota():
    q = db.GqlQuery("SELECT sender_email, amount FROM Donation"
                    " WHERE status = 'COMPLETED' AND amount > 0")
    key = operator.attrgetter('sender_email')
    all_ = sorted(q, key=key)
    sums = sorted(sum(d.amount for d in donations) for email, donations in
                  itertools.groupby(all_, key=key))
    d = {r: len(list(s)) for r, s in itertools.groupby(sums, get_rank)}
    for a, r in ranks:
        yield (a, d.get(r, 0))


def date_amount_query(email):
    return db.GqlQuery("SELECT date, amount FROM Donation"
                       " WHERE sender_email = :email AND"
                       " status = 'COMPLETED' AND amount > 0",
                       email=email)


def get_donated_and_discount(email):
    "Returns tuple of total donated amount and discount (%)"
    q = date_amount_query(email)
    stopdate = datetime.datetime(year=2012, month=11, day=5)
    total = sum(d.amount for d in q)
    discountable = sum(d.amount for d in q if d.date <= stopdate)
    if discountable >= 2000:
        discount = decimal.Decimal('0.2')
    elif discountable >= 1000:
        discount = decimal.Decimal('0.1')
    else:
        discount = decimal.Decimal(0)
    tshirt = bool(sum(d.amount for d in q if d.date > stopdate) >= 1000)
    return total, discount, tshirt


def get_donations(email):
    for d in date_amount_query(email):
        yield d.date, d.amount


